package ar.edu.itba.it.cg.grupo01.impl.shape;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;

import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import ar.edu.itba.it.cg.grupo01.api.Intersection;
import ar.edu.itba.it.cg.grupo01.api.Ray;
import ar.edu.itba.it.cg.grupo01.api.shape.Shape;
import ar.edu.itba.it.cg.grupo01.impl.IntersectionFactory;
import ar.edu.itba.it.cg.grupo01.impl.Precision;

public class Plane extends AbstractShape {

	public Vector3d normal;
	public Point3d point;
	
	public Plane(Point3d aPoint, Vector3d normal,
			IntersectionFactory intersectionFactory) {
		super(intersectionFactory);
		this.normal = ((Vector3d) normal.clone());
		this.normal.normalize();
		this.point = (Point3d) aPoint.clone();
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		Plane other = (Plane) obj;
		double angle = other.normal.angle(normal);
		if (Math.abs(angle - Math.PI) < Precision.ANGLE_EPSILON
				|| Math.abs(angle) < Precision.ANGLE_EPSILON) {
			if (this.contains(other.point)) {
				return true;
			}
		}

		return false;
	}

	
	/**Checks if a point belongs to a plane
	 * @param point2 
	 * @return
	 */
	private boolean contains(Point3d point2) {
		Vector3d auxVec = new Vector3d(normal);
		double belong = Math.abs(auxVec.dot(new Vector3d(point2))
				+ this.getDistanceToOrigin());
		if(Math.abs(belong)<Precision.EPSILON){
			return true;
		}
		return false;
	}



	

	@Override
	public Intersection<? extends Shape> intersects(final Ray ray) {

		Collection<Double> intersections = findIntersections(ray);

		if (intersections.isEmpty()) {
			return intersectionFactory.emptyIntersection();
		}

		List<Point3d> points = new ArrayList<Point3d>();
		for (Double result : intersections) {

			Point3d point = ray.calculatePoint(result);
			points.add(point);
		}
		return intersectionFactory.createIntersection(points);
	}


	private Collection<Double> findIntersections(Ray ray) {

		Collection<Double> intersectionPoints = new HashSet<Double>();

		// First compute Pn Rd = Vd
		double vd = this.normal.dot(ray.getDirection());
		if (vd == 0.0)
			// the ray is parallel to the plane and there is no intersection (if
			// ray in in the plane then we ignore it) empty set
			return Collections.emptySet();

		// V0 = -(Pn R0 + D)
		double v0 = -(normal.dot(new Vector3d(ray.getOrigin())) + this
				.getDistanceToOrigin());

		double t = v0 / vd;

		if (t > 0) {
			intersectionPoints.add(t);
			return intersectionPoints;
		} else
			return Collections.emptySet();

	}

	/*
	 * If plane is represented as ax+by+cz+d=0 d is distance to [0,0,0]
	 */
	public double getDistanceToOrigin() {
		Vector3d auxVec = new Vector3d(normal);
		return (-auxVec.dot(new Vector3d(point)));
	}

}
